#include <numeric>
#include <iostream>
#include <limits>
#include <map>
using namespace std;
#include "main.h"
#include "connection.h"
#include "player.h"
#include "scope.h"
#include "server.h"
#include "log.h"
#include "file.h"
#include "packageformat.h"
#include "packageformatex.h"
#include "sicboComm.h"
#include "RCtrl.h"

#include <cv.h>
#include <highgui.h>
#include <QApplication>
#include <QPushButton>
#include <QtXml>
#include <boost/filesystem/operations.hpp>
#include <boost/random.hpp>
#define TEST 0 
#define TEST_PLAYER 0
#define TEST_CONNECTION 0 
#define TEST_SCOPE 0
#if TEST_CONNECTION
	#include "connection.h"
#endif
#if TEST_SCOPE 
	#include "scope.h"
#endif
string Path;
vector<int> ports;
vector<QString> hosts;
int BINARY_THRESHOLD = 43, width_offset = 5, size_offset = 5;
float group_offset = 50.0f;
CvRect effectArea = cvRect(50, 50, 100, 100);
int simPoints = 1, usingNetwork = 0, staticPic = 0;
int bettingInterval = 15, showdownInterval = 15;
bool recognizing = false, ctrlByServer = false;
IplImage *errImg = 0, *errImg1 = 0, *errImg2 = 0; 
class ConfigParser : public QXmlDefaultHandler
{
public:
	bool startElement( const QString&, const QString&, const QString& qName,
                       const QXmlAttributes& attr)
	{
		if (qName.compare("server", Qt::CaseInsensitive) == 0) {
			QString host = attr.value("host");
			int port = attr.value("port").toInt();
			hosts.push_back(host);
			ports.push_back(port);
			qDebug() << "host=" << host;
			qDebug() << "port=" << QString::number(port);
		}
		if (qName.compare("param", Qt::CaseInsensitive) == 0) {
			BINARY_THRESHOLD = attr.value("threshold").toInt(); 
			group_offset = attr.value("group_offset").toFloat();
			width_offset = attr.value("width_offset").toInt();
			size_offset = attr.value("size_offset").toInt();
			effectArea.x = attr.value("effectArea_x").toInt();
			effectArea.y = attr.value("effectArea_y").toInt();
			effectArea.width = attr.value("effectArea_width").toInt();
			effectArea.height = attr.value("effectArea_height").toInt();	
			qDebug() << "threshold=" << QString::number(BINARY_THRESHOLD);
			qDebug() << "group_offset=" << QString::number(group_offset);
		}
		if (qName.compare("options", Qt::CaseInsensitive) == 0) {
			simPoints = attr.value("simPoints").toInt();
			usingNetwork = attr.value("usingNetwork").toInt(); 
			staticPic = attr.value("staticPic").toInt();
			bettingInterval = attr.value("bettingInterval").toInt();
			qDebug() << "bettingInterval=" << QString::number(bettingInterval);
			showdownInterval = attr.value("showdownInterval").toInt();
			qDebug() << "showdownInterval=" << QString::number(showdownInterval);
			ctrlByServer = attr.value("ctrlByServer").toInt();  
		}
	}
private:
};

void load_config(const QString &filename)
{
	ConfigParser handler;
	QFile xmlFile(filename);
	QXmlInputSource source(&xmlFile);
	QXmlSimpleReader reader;
	reader.setContentHandler( &handler );
	reader.parse( source );
}
void save_config(const QString &filename)
{
	QFile xmlFile(filename);
	if (!xmlFile.open(QFile::ReadWrite | QFile::Text)) {
		cerr << "Error: Cannot write file " <<
			qPrintable(filename) << ": " <<
			qPrintable(xmlFile.errorString()) << endl;
			return;
	}
	QXmlStreamWriter stream(&xmlFile);
	stream.setAutoFormatting(true);
	stream.writeStartDocument();
	stream.writeStartElement("document");

	int count = hosts.size();
	for(int i = 0; i < count; i++) {
		QString host = hosts.at(i);
		int port = ports.at(i);
		stream.writeStartElement("server");
		stream.writeAttribute("host", host);
		stream.writeAttribute("port", QString::number(port));
		stream.writeEndElement(); 
	}

	stream.writeStartElement("options");
	stream.writeAttribute("simPoints", QString::number(simPoints));
	stream.writeAttribute("usingNetwork", QString::number(usingNetwork));
	stream.writeAttribute("staticPic", QString::number(staticPic));
	stream.writeAttribute("bettingInterval", QString::number(bettingInterval));
	stream.writeAttribute("showdownInterval", QString::number(showdownInterval));
	stream.writeAttribute("ctrlByServer", QString::number(ctrlByServer));  
	stream.writeEndElement(); 
	
	stream.writeStartElement("param");
	stream.writeAttribute("threshold", QString::number(BINARY_THRESHOLD));
	stream.writeAttribute("group_offset", QString::number(group_offset)); 
	stream.writeAttribute("width_offset", QString::number(width_offset)); 
	stream.writeAttribute("size_offset", QString::number(size_offset)); 
	stream.writeAttribute("effectArea_x", QString::number(effectArea.x)); 
	stream.writeAttribute("effectArea_y", QString::number(effectArea.y)); 
	stream.writeAttribute("effectArea_width", QString::number(effectArea.width)); 
	stream.writeAttribute("effectArea_height", QString::number(effectArea.height)); 
	stream.writeEndElement(); 

	stream.writeEndElement(); 
	stream.writeEndDocument();
}
vector<CvRect> all_points;
void drawEffectArea(IplImage *img)
{
	cvRectangle(img, cvPoint(effectArea.x, effectArea.y), 
		cvPoint(effectArea.x + effectArea.width,
			effectArea.y + effectArea.height), 
		CV_RGB(255, 0, 0));
}
bool debugWindows = false;
int dicePoints(IplImage *img)
{
	CvSize size = cvSize(img->width, img->height);
	IplImage *imgTmp = cvCreateImage(size, img->depth, 1);
	assert(imgTmp != 0);
	IplImage *img2 = cvCreateImage(size, img->depth, 1);
	assert(img2 != 0);
	cvCvtColor(img, img2, CV_BGR2GRAY);
	cvCvtColor(img, imgTmp, CV_BGR2GRAY);
	cvThreshold(imgTmp, img2, BINARY_THRESHOLD, 255, CV_THRESH_BINARY);
	if (debugWindows)
		cvShowImage("sicbo2", img2);
	CvMemStorage *storage = cvCreateMemStorage(0);
	CvSeq *contour;
	cvFindContours(img2, storage, &contour, sizeof(CvContour),
		CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE);
	IplImage *img3 = cvCreateImage(size, img->depth, 3);
	assert(img3 != 0);
	CvSeq *begin = contour;
	//Disproportionate size of the removed
	int maxW = 0, maxH = 0;
	int offset = width_offset;
	for(; contour; contour = contour->h_next) {
		CvRect rect = cvBoundingRect(contour, 1);
		if (abs(rect.width - rect.height) > offset)
			continue;
		if (rect.width > maxW)
			maxW = rect.width;
		if (rect.height > maxH)
			maxH = rect.height;	
	}
	offset = size_offset;
	for(contour = begin; contour; contour = contour->h_next) {
		CvRect rect = cvBoundingRect(contour, 1);
		if (!(rect.x > effectArea.x && rect.y > effectArea.y &&
			rect.x < effectArea.x + effectArea.width &&
			rect.y < effectArea.y + effectArea.height))
			continue;
		if (abs(rect.width - maxW) > offset)
			continue;
		if (abs(rect.height - maxH) > offset)
			continue;
		all_points.push_back(rect);
		cvDrawContours(img3, contour, CV_RGB(255,0,0),
			CV_RGB(255,0,0), 0, 2, 8);
	}

	IplImage *imgs[] = {
		img, img3
	};
	int count = dimof(imgs);
	for(int i = 0; i < count; i++)	
		drawEffectArea(imgs[i]);
	if (recognizing) {	
		cvSetImageROI(img3, effectArea);
		cvSetImageROI(errImg2, effectArea);
		cvCopy(img3, errImg2);
		cvResetImageROI(img3);
		cvResetImageROI(errImg2);
	}
	if (debugWindows)
		cvShowImage("sicbo3", img3);
	cvReleaseImage(&imgTmp);
	cvReleaseImage(&img2);
	cvReleaseImage(&img3);
	cvReleaseMemStorage(&storage);
	return all_points.size();
}
namespace GLOBAL {
STATUS status = sNone;
};
QTime timer;
STATUS getStatus()
{
	return GLOBAL::status;
}
void setStatus(STATUS s)
{
	qDebug() << QString::fromStdString(statusDesc.at(s));
	GLOBAL::status = s;
	timer.restart();
}
boost::mt19937 rng(static_cast<unsigned>(std::time(0)));
struct my_rand {
	int operator()(unsigned int i)
	{
		boost::uniform_int<> r(0, i - 1);
		boost::variate_generator<boost::mt19937 &, boost::uniform_int<> > gen(rng, r);
		return gen();
	}
};
bool send(shared_ptr<CSentence> s, int target = -1)
{
	if (usingNetwork) {
		string str("sicboLive");
		s->add(tgType)->set(str.c_str());
		int count = hosts.size();
		for(int i = 0; i < count; i++) {
			if (target > -1 && i != target)
				continue;
			QString host = hosts.at(i);
			int port = ports.at(i);
			toServer(host, port, s);
		}
	}
	return true;
}
void notify_initial(int i)
{
	shared_ptr<CSentence> s(new CSentence);
	s->tag(tkExternal);
	STATUS status = sInitial;
	s->add(tgStatus)->set(&status);
	s->add(tgTime)->set(&bettingInterval);
	send(s, i);
}
void notify_bet()
{
	shared_ptr<CSentence> s(new CSentence);
	s->tag(tkExternal);
	STATUS status = sBetting;
	setStatus(status);
	s->add(tgStatus)->set(&status);
	send(s);
}
void notify_result(const vector<int> &dices)
{
	shared_ptr<CSentence> s(new CSentence);
	s->tag(tkExternal);
	STATUS status = sShowdown;
	setStatus(status);
	s->add(tgStatus)->set(&status);
	DICE dice;
	bool succ = dices.size() == 3;
	if (succ) {
		int count = accumulate(dices.begin(), dices.end(), 0);
		succ = count >= 3 && count <= 18;
	} else if (errImg != 0){
		static int i;
		stringstream ss;
		ss << Path << "/jpg/errorA" << i << ".jpg";
		cvSaveImage(ss.str().c_str(), errImg);
		ss.str("");
		ss << Path << "/jpg/errorB" << i << ".jpg";
		cvSaveImage(ss.str().c_str(), errImg1);
		ss.str("");
		ss << Path << "/jpg/errorC" << i << ".jpg";
		cvSaveImage(ss.str().c_str(), errImg2);
		i++;
	}
	qDebug() << "notify_result succ=" << succ;
	if (succ) {
		dice.d1 = dices.at(0);
		dice.d2 = dices.at(1);
		dice.d3 = dices.at(2);
		s->add(tgNumber)->set(&dice);
	} else 
		s->add(tgMessage)->set("recognition error");
	send(s);
}
void showdown(const vector<int> &dices)
{
	if (dices.size() == 3) {	
		qDebug() << QString::number(dices.at(0)) << ":" <<
			QString::number(dices.at(1)) << ":" <<
			QString::number(dices.at(2));
	}
	notify_result(dices);
}
bool learning = false;
float distance_(const CvRect &r1, const CvRect &r2)
{
	return sqrt(pow((r2.x - r1.x), 2) + pow((r2.y - r1.y), 2));
}
void get_lines(const vector<CvRect> &v, vector<float> &lines)
{
	int count = v.size();
	for(int i = 0; i < count; i++) {
		const CvRect &r1 = v.at(i);
		for(int j = i + 1; j < count; j++) {
			const CvRect &r2 = v.at(j);
			float length = distance_(r1, r2);
			lines.push_back(length);
		}
	}
}
void getMaxDistance(const vector<CvRect> &v, CvRect &cr1, CvRect &cr2)
{
	float max = 0;
	int count = v.size();
	for(int i = 0; i < count; i++) {
		const CvRect &r1 = v.at(i);
		if (count < 2) {
			cr1 = cr2 = r1;
		}
		for(int j = i + 1; j < count; j++) {
			const CvRect &r2 = v.at(j);
			float length = distance_(r1, r2);
			if (length > max) {
				max = length;
				cr1 = r1;
				cr2 = r2;
			}
		}
	}
}
vector<CvRect> group_points(const vector<CvRect> &points, int target)
{
	int bits = points.size();
	assert(bits > 0 && bits < 19);
	int count = pow(2, bits) - 1;
	vector<int> v;
	for(int i = pow(2, target) - 1; i <= count; i++) {
		int count = 0;
		for(int j = 0; j < bits && count <= target; j++) {
			int b = pow(2, j);
			if ((i & b) == b) {
				count++;
			}
		}
		if (count == target) {
			v.push_back(i);
		}
	}
	bool found = false;
	vector<CvRect> tmp;
	count = v.size();
	for(int i = 0; i < count && !found; i++) {
		tmp.clear();
		int n = v.at(i);
		for(int j = 0; j < bits; j++) {
			int b = pow(2, j);
			const CvRect &r = points.at(j);
			if ((n & b) == b) {
				tmp.push_back(r);
			} 
		}
		vector<float> lines;
		get_lines(tmp, lines);
		vector<float>::iterator itr = max_element(lines.begin(), lines.end());
		if (itr == lines.end())
			continue;
		found = *itr < group_offset;
	}
	if (!found)
		tmp.clear();
	return tmp;
}
void remove(vector<CvRect> &dice, const CvRect &rect)
{
	vector<CvRect>::iterator i = dice.begin();
	for(; i != dice.end(); i++) {
		const CvRect &r = *i;
		if (r.x == rect.x && r.y == rect.y && 
			r.width == rect.width &&
			r.height == rect.height) {
			dice.erase(i);
			break;
		}
	}
}
void remove(vector<CvRect> &all_points, vector<CvRect> &dice)
{
	int count = dice.size();
	for(int i = 0; i < count; i++) {
		const CvRect &rect = dice.at(i);
		remove(all_points, rect);
	}
}
void paint_dice(IplImage *img, vector<CvRect> &dice, int color)
{
	CvScalar c;
	switch(color)
	{
		case 0:
			c = CV_RGB(255,0,0);
			break;
		case 1:
			c = CV_RGB(0,255,0);
			break;
		case 2:
			c = CV_RGB(0,0,255);
			break;
		case 3:
			c = CV_RGB(255,255,255);
			break;
	}
	int count = dice.size();
	for(int i = 0; i < count; i++) {
		const CvRect &rect = dice.at(i);
		CvPoint pt1 = cvPoint(rect.x, rect.y),
			pt2 = cvPoint(rect.x + rect.width,
				rect.y + rect.height);
		cvRectangle(img, pt1, pt2, c, 1);
	}
	CvRect cr1, cr2;
	getMaxDistance(dice, cr1, cr2);
	CvPoint CircleCenter = cvPoint(cr1.x - (cr1.x - cr2.x) / 2,
		cr1.y - (cr1.y - cr2.y) / 2);
	float r = group_offset / 2.0f;
	cvCircle(img, CircleCenter, r, c, 1, CV_AA, 0);
}
int analyze(IplImage *img, vector<int> &dices)
{
	all_points.clear();
	int p = dicePoints(img);
	CvSize size = cvSize(img->width, img->height);
	int count = all_points.size();
	IplImage *img3 = cvCreateImage(size, img->depth, 3);
	assert(img3 != 0);
	for(int j = 6; j > 1 && count > 0 && count < 19; j--) {//2~6 points
		for(int i = 0; i < 3 && count > 0; i++) {//three dices
			vector<CvRect> dice = group_points(all_points, j);
			count = dice.size();
			if (count > 0) {
				remove(all_points, dice);
				paint_dice(img3, dice, dices.size());
				dices.push_back(count);
			}
			count = all_points.size();
		}
	}
	count = all_points.size();
	for(int i = 0; i < count; i++) {
		const CvRect &r = all_points.at(i);
		vector<CvRect> dice;
		dice.push_back(r);
		paint_dice(img3, dice, dices.size());//1 poinit
		dices.push_back(1);
	}
	drawEffectArea(img3);
	if (recognizing) {	
		cvSetImageROI(img3, effectArea);
		cvSetImageROI(errImg, effectArea);
		cvCopy(img3, errImg);
		cvResetImageROI(img3);
		cvResetImageROI(errImg);
		
		cvSetImageROI(img, effectArea);
		cvSetImageROI(errImg1, effectArea);
		cvCopy(img, errImg1);
		cvResetImageROI(img);
		cvResetImageROI(errImg1);
	}
	if (debugWindows)
		cvShowImage("sicbo4", img3);
	cvReleaseImage(&img3);
	return dices.size();
}
vector<int> result;
vector<bool> initialed;
int convert(int i)
{
	Assert(i > 0 && i <= 6);
	int ret;
	switch(i) {
		case 1:
			ret = 6;
			break;
		case 2:
			ret = 5;
			break;
		case 3:
			ret = 4;
			break;
		case 4:
			ret = 3;
			break;
		case 5:
			ret = 2;
			break;
		case 6:
			ret = 1;
			break;
	}
	return ret;
}
void convert(vector<int> &dices)
{
	int count = dices.size();
	for(int i = 0; i < count; i++) {
		int &v = dices.at(i);
		v = convert(v);
	}
}
int processing = 0;
map<QString, int> resultList;//map<result, count>
int errCount = 0;
vector<int> start(CvCapture *capture)
{
	vector<int> dices;
	int count = 0;
	if (capture || staticPic) {
		IplImage *img;
		if (staticPic) {
			img = cvLoadImage("./static.jpg", CV_LOAD_IMAGE_ANYCOLOR);
		} else
			img = cvQueryFrame(capture);
		if (staticPic || !usingNetwork)
			recognizing = true;
		//CvSize size = cvSize(effectArea.width * 2, effectArea.height);
		CvSize size = cvSize(img->width, img->height);
		bool renew = errImg == 0 || errImg->width != size.width ||
			errImg->height != size.height; 
		if (renew) {
			if (errImg) {
				cvReleaseImage(&errImg);
				cvReleaseImage(&errImg1);
				cvReleaseImage(&errImg2);
			}
			
			errImg = cvCreateImage(size, img->depth, 3);
			errImg1 = cvCreateImage(size, img->depth, 3);
			errImg2 = cvCreateImage(size, img->depth, 3);
		} 	
		if (img) {
			count = analyze(img, dices);
			if (count) {
				convert(dices);
			}
			if (debugWindows)
				cvShowImage("sicbo1", img);
			if (staticPic) {
				cvReleaseImage(&img);
				result = dices;
			}
		}
	} else {
		//for(int i = 0; i < 3 && getStatus() == sBetting; i++)
			//dices.push_back(my_rand()(6) + 1); 
		static int i, j;
		i = i % 6 + 1;
		if (i == 1)
			j = j % 6 + 1;
		dices.push_back(1);
		dices.push_back(i);
		dices.push_back(j);
		count = dices.size();
		result = dices;
	}
	int total_points = 0;
	if (recognizing) {
		bool succ = count == 3;
		for(int i = 0; i < count && succ; i++) {
			int p = dices.at(i);
			succ = p > 0 && p < 7;
		}
		if(succ) {
			int count = accumulate(dices.begin(), dices.end(), 0);
			if (count >= 3 && count <= 18)
				total_points = count;
		} else
			errCount++;
	} else
		errCount = 0; 
	int maxTimes = 0;
	if (total_points > 0 && !staticPic) {
		QString str;
		for(int i = 0; i < count; i++) 
			str.append(QString::number(dices.at(i)));
		typedef map<QString, int>::iterator iterator;
		typedef map<QString, int>::value_type value_type;
		typedef pair<iterator, bool> pair;
		iterator i = resultList.find(str);
		if (i != resultList.end()) {
			i->second++;
		} else {
			pair p = resultList.insert(value_type(str, 1));
			assert(p.second == true);
		}
		i = resultList.begin();
		iterator max = i;
		for(; i != resultList.end(); i++) {
			if (i->second > max->second)
				max = i;
		}
		if (max != resultList.end()) {
			const QString &m = max->first;
			maxTimes = max->second;
			if (maxTimes > errCount) { 
				dices.clear();
				count = m.size();
				for(int i = 0; i < count; i++) {
					const QChar ch = m.at(i);
					int n = ch.digitValue();
					dices.push_back(n);
				}
				result = dices;
			} else {
				result.clear();
			}
		}
	} 
	return result;	
}
QTextCodec *codec;
QLineEdit *ledPassword;
MyWidget::MyWidget(QWidget *parent)
		: QWidget(parent)
{
	max_connections = 0;
	//setFixedSize(200, 120);
	QObject::startTimer(100);
	
	label1 = new QLabel("Binary threshold", this);
	label1->setGeometry(0, 0, 200, 30);
	QFont font(QFont("Times", 12, QFont::Bold));
	label1->setFont(font);
	spinbox = new QSpinBox(this);
	spinbox->setGeometry(200, 0, 50, 30);
	spinbox->setRange(0, 255);
	spinbox->setValue(BINARY_THRESHOLD);
	//spinbox->setPrefix(QString::number(BINARY_THRESHOLD)); 
	QObject::connect(spinbox, SIGNAL(valueChanged(int)), this, SLOT(thresholdChanged(int)));
	

	label1 = new QLabel("effect area x", this);
	label1->setGeometry(0, 30, 200, 30);
	label1->setFont(font);
	spinbox = new QSpinBox(this);
	spinbox->setGeometry(200, 30, 50, 30);
	spinbox->setRange(0, 800);
	spinbox->setValue(effectArea.x);
	QObject::connect(spinbox, SIGNAL(valueChanged(int)), this, SLOT(effectAreaXChange(int)));

	label1 = new QLabel("effect area y", this);
	label1->setGeometry(0, 60, 200, 30);
	label1->setFont(font);
	spinbox = new QSpinBox(this);
	spinbox->setGeometry(200, 60, 50, 30);
	spinbox->setRange(0, 600);
	spinbox->setValue(effectArea.y);
	QObject::connect(spinbox, SIGNAL(valueChanged(int)), this, SLOT(effectAreaYChange(int)));
	
	label1 = new QLabel("effect area width", this);
	label1->setGeometry(0, 90, 200, 30);
	label1->setFont(font);
	spinbox = new QSpinBox(this);
	spinbox->setGeometry(200, 90, 50, 30);
	spinbox->setRange(0, 800);
	spinbox->setValue(effectArea.width);
	QObject::connect(spinbox, SIGNAL(valueChanged(int)), this, SLOT(effectAreaWidthChange(int)));

	label1 = new QLabel("effect area height", this);
	label1->setGeometry(0, 120, 200, 30);
	label1->setFont(font);
	spinbox = new QSpinBox(this);
	spinbox->setGeometry(200, 120, 50, 30);
	spinbox->setRange(0, 600);
	spinbox->setValue(effectArea.height);
	QObject::connect(spinbox, SIGNAL(valueChanged(int)), this, SLOT(effectAreaHeightChange(int)));

	label1 = new QLabel("group offset", this);
	label1->setGeometry(0, 150, 200, 30);
	label1->setFont(font);
	spinbox = new QSpinBox(this);
	spinbox->setGeometry(200, 150, 50, 30);
	spinbox->setRange(0, 1000);
	spinbox->setValue(group_offset * 10);
	spinbox->setSingleStep(5);
	QObject::connect(spinbox, SIGNAL(valueChanged(int)), this, SLOT(groupOffsetChange(int)));
	throwDices = new QPushButton("start throwing", this);
	throwDices->setGeometry(250, 0, 100, 30);
	QObject::connect(throwDices, SIGNAL(clicked()), this, SLOT(startThrowing()));
	showDices = new QPushButton("check state", this);
	showDices->setGeometry(250, 30, 100, 30);
	QObject::connect(showDices, SIGNAL(clicked()), this, SLOT(startShowing()));
	label2 = new QLabel(this);
	label2->setGeometry(250, 60, 100, 30);

	codec = QTextCodec::codecForName("UTF-8");
	showDebug = new QPushButton(codec->toUnicode("顯示除錯視窗"), this);
	showDebug->setGeometry(250, 180, 100, 30);
	QObject::connect(showDebug, SIGNAL(clicked()), this, SLOT(showDebugWindows()));
	
	ledPassword = new QLineEdit(this);
	ledPassword->setEchoMode(QLineEdit::Password);
	ledPassword->setGeometry(0, 180, 150, 30);

	HWND h = this->winId();
	if (!simPoints && !staticPic) {
		SetHWND(h);
		SetRobot(ROBOT_GOHOME);    
		processing = ROBOT_GOHOME;
		throwDices->setDisabled(true);
		showDices->setDisabled(true);
		qDebug() << "robot gohome";
	}

	/*
	QTextCodec *codec = QTextCodec::codecForName("Big5");
	assert(codec != 0);
	QLabel label("Hello! world! Orz...");
	str = codec->toUnicode("HiHi");
	label.setWindowTitle(str);
	label.resize(200, 50);
	label.show();
	*/
	this->resize(400, 250);
}
void MyWidget::reload_act()
{
}
void MyWidget::thresholdChanged(int i)
{
	BINARY_THRESHOLD = i;
}
void MyWidget::effectAreaXChange(int i)
{
	effectArea.x = i;
}
void MyWidget::effectAreaYChange(int i)
{
	effectArea.y = i;
}
void MyWidget::effectAreaWidthChange(int i)
{
	effectArea.width = i;
}
void MyWidget::effectAreaHeightChange(int i)
{
	effectArea.height = i;
}
void MyWidget::groupOffsetChange(int i)
{
	group_offset = i / 10.0f;
}
bool processed = false, succ = false;
void MyWidget::startThrowing()
{
	setStatus(sRolling);
	if (simPoints) {
		notify_bet();
	} else if (!staticPic) {
		SetRobot(OPEN_BANKER3);//開始骰
		qDebug() << "startThrowing";
		throwDices->setDisabled(true);
		showDices->setDisabled(true);
		processing = OPEN_BANKER3;
		recognizing = false;
		resultList.clear();
		result.clear();
	}
}
void MyWidget::startShowing()
{
	setStatus(sShowdown);
	if (simPoints) {
		showdown(result);
	} else if (!staticPic) {
		SetRobot(OPEN_BANKER2);//開牌
		qDebug() << "startShowing";
		throwDices->setDisabled(true);
		showDices->setDisabled(true);
		processing = OPEN_BANKER2;
		recognizing = false;
	}
}
void MyWidget::showDebugWindows()
{
	QString str = ledPassword->text();
	debugWindows ^= true;
	if (debugWindows) {
		bool succ = str.compare(QString::number(20110823)) == 0;
		debugWindows = succ;
		if (!succ) {
			QMessageBox messageBox(this);
			QString str = codec->toUnicode("密碼錯誤"); 
			QAbstractButton *okButton = messageBox.addButton(str, QMessageBox::ActionRole);
			messageBox.exec();
		} 
	} 
	if (debugWindows) {
		showDebug->setText(codec->toUnicode("關閉除錯視窗"));
		if (!simPoints || staticPic) {
			for(int i = 1; i < 5; i++) {
				stringstream ss;
				ss << "sicbo" << i;
				cvNamedWindow(ss.str().c_str(), 1);
			}
		}
	} else {
		showDebug->setText(codec->toUnicode("顯示除錯視窗"));
		for(int i = 1; i < 5; i++) {
			stringstream ss;
			ss << "sicbo" << i;
			cvDestroyWindow(ss.str().c_str());
		}
	}
}
CvCapture *capture = 0;
void on_deal(MyWidget *widget)
{
	shared_ptr<CSentence> s = connection::pop();
	if (!ctrlByServer) { 
		int elapsed = timer.elapsed();
		switch (getStatus()) {
			case sNone:
				if (!TEST) {
					widget->startThrowing();
				}
				break;
			case sBetting:
				if (elapsed > (bettingInterval + 2) * 1000) {
					if (!TEST) {
						widget->startShowing();
					}
				}
				break;
			case sShowdown:
				if (elapsed > showdownInterval * 1000) {
					setStatus(sNone);
				}
				break;
		}
	} else if (s.get() != 0 && s->tag() == tkSicbo) {
		shared_ptr<CSentence> sub = s->find(tgOrder);
		if (sub.get() != 0) {
			int st = sub->get<int>();
			qDebug() << "on_deal:" << QString::fromStdString(statusDesc.at(st));
			switch(st) {
				case sRolling:
					if (!TEST) {
						widget->startThrowing();
					}
					break;
				case sShowdown:
					if (!TEST) {
						widget->startShowing();
					}
					break;
			}
		}	
	}
}
void network(MyWidget *widget)
{
	int count = hosts.size();
	initialed.resize(count);
	for(int i = 0; i < count; i++) {
		QString host = hosts.at(i);
		int port = ports.at(i);
		if (!connected(host, port)) {
			initialed.at(i) = false;
			if (!connecting(host, port)) {
				qDebug() << "connecting " << host << ":" << port;
				start_connect(host, port);
			}
			//boost::this_thread::sleep(workTime);
		} else if (!initialed.at(i)) {
			notify_initial(i);
			initialed.at(i) = true;
		}
	}
	if (!processing) 
		on_deal(widget);
}
void MyWidget::timerEvent(QTimerEvent *)
{
	if (usingNetwork)
		network(this);
	vector<int> dices = start(capture);
	if (recognizing) {
		QString str;
		int count = dices.size();
		if (count == 3) {
			for(int i = 0; i < count; i++) {
				int n = dices.at(i);
				str.append(QString::number(n));
			}
			label2->setText(str);
		} else 
			label2->setText("error");
	} else {
			label2->setText(QString::fromStdString(statusDesc.at(getStatus())));
	}
	if (processed) {
		switch(processing) {
			case OPEN_BANKER3:
				if (succ) {
					recognizing = true;
					notify_bet();
				} else {
				}
				break;
			case OPEN_BANKER2: 
				if (succ) {
					showdown(result);
				} else {
				}
				break;
		}
		throwDices->setDisabled(false);
		showDices->setDisabled(false);
		processing = 0;
		processed = false;
	}
}
bool MyApp::winEventFilter(MSG *m, long *result)
{
	int msgType = m->message;
	switch(msgType) {
		case RB_READY:
			qDebug() << "RB_READY";
			succ = true;
			processed = true;
			break;
		case RB_ERROR:
		case RB_POWER:
			qDebug() << "RB_ERROR or RB_POWER";
			succ = false;
			processed = true;
			break;
	}
	return false;
}
int main(int argc, char **argv)
{
	int ret = 0;
#ifdef _DEBUG
	qDebug() << "debug version";
#endif
#ifdef NDEBUG
	qDebug() << "release version";
#endif

	load_config("config.xml");	
	boost::filesystem::path p = boost::filesystem::current_path();
	Path = p.string();
	QString str = QString::fromStdString(Path);
	qDebug() << "path=" << str;
	str += "/";
	str += "log";
	simone::path_log(str.toStdString());
	str = QString::fromStdString(Path);
	str += "/";
	str += "jpg";
        mkdir(str.toStdString());

	if (debugWindows) {
		if (!simPoints || staticPic) {
			for(int i = 1; i < 5; i++) {
				stringstream ss;
				ss << "sicbo" << i;
				cvNamedWindow(ss.str().c_str(), 1);
			}
		}
	}
	if (!simPoints && !staticPic) {
		capture = cvCreateCameraCapture(0);
		assert(capture != 0);
	}
	
	MyApp app(argc, argv);
	MyWidget widget;
	QPushButton quit("Quit", &widget);
	quit.setGeometry(250, 120, 100, 30);
	quit.setFont(QFont("Times", 12, QFont::Bold));
	QObject::connect(&quit, SIGNAL(clicked()), &app, SLOT(quit()));
	widget.show();
	ret = app.exec();
	if (errImg) {
		cvReleaseImage(&errImg);
		cvReleaseImage(&errImg1);
		cvReleaseImage(&errImg2);
	}
	if (capture)
		cvReleaseCapture(&capture);
	save_config("config.xml");
	return ret;
}
